package 函数式编程.使用Stream;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.BaseStream;
import java.util.stream.Collector;
//import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.Collector.Characteristics;
/*
Collector接口与Collectors类关系：
以Stream接口方法collect为例：这个接口有三种类型，T代表流中元素的类型，A是中间结果容器的类型，R是最后返回的类型
-Stream接口方法：<R, A> R collect(Collector<? super T, A, R> collector);//方法参数为Collector接口实例

-Collectors类toList方法：（返回Collector实例）
public static <T> Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>(ArrayList::new, List::add,(left, right) -> { left.addAll(right); return left; },CH_ID);
}
	--Collectors类的静态类:static class CollectorImpl<T, A, R> implements Collector<T, A, R>
		之构造函数：CollectorImpl(Supplier<A> supplier,
                      BiConsumer<A, T> accumulator,
                      BinaryOperator<A> combiner,
                      Function<A,R> finisher,
                      Set<Characteristics> characteristics)

将Stream转成List：
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> squaresList = numbers.stream().map(i -> i * i).sorted((x, y) -> y - x).collect(Collectors.toList());



【Collector接口的组成】

一、Collector常常出现的地方
java8引入了stream，Collector是与stream一起出现的，配合stream使用的好帮手，如果用过stream，我们应该都有写过这样的代码

public interface Stream<T> extends BaseStream<T, Stream<T>> {
	...
	<R, A> R collect(Collector<? super T, A, R> collector);
	...
}
在Stream接口多个方法
例子1：lists.stream()....collect(Collectors.toList());//Collectors.toList()返回Collector实例
例子2：lists.stream().collect(groupingBy(String::length));
这两个例子中，toList()和groupingBy()返回的都是一个Collector对象,那么问题来了，什么是Collector?

二、什么是Collector
Collector其实是一个泛型接口，通过这个接口可以定义一系列的聚合操作，按照官方文档的说法，
Collector其实是提供mutable reduction operation，即可改变的减少操作。(mutable:可变的)

常见的聚合操作有：

1.用StringBuilder拼接字符串

2.计算元素综合的数据，比如sum, min, max, or average
这个reduction在Google翻译是减少的意思，但是我不太对得上这个意思，觉得形容成聚合操作会更容易理解一点。

关于聚合操作，我们会在很多语言中遇到，比如mysql里面的group by操作，sum()，min()，max()，Count()，anyValue()，这些叫
做aggregate function，即聚合操作

我理解这些操作的是类似的，只不过这些是在数据库里面进行的，collector是在java代码层进行的，他们的本质都是一样的，他们都进行了多对一的转换，将一
系列的数据变成一个数据或者几团数据。

三、Collector的使用
Collector是一个接口，它还有一个静态工具类Collectors，Collectors提供了很多常见的聚合操作的实现，通常来说我们调用Collectors里面的方法就
够了，如果想要更多更复杂的实现也可以自定义一个collector，定义Collector的话，我们需要先了解Collector的组成

3.1 Collector的泛型类型
collector是一个泛型接口，那我们先从泛型的元素开始分析

Collector<T, A, R>

这个接口有三种类型，T代表流中元素的类型，A是中间结果容器的类型，R是最后返回的类型
比如一个字符串数组strings，对它进行这个操作

strings.stream()....collect(Collectors.toList());
toList()方法返回的Collector中，T就是String类型，A是List<String>类型，R是List<String>类型，如果不能理解可以继续往下看

3.2 Collector 的组成
public interface Collector<T, A, R> {
   Supplier<A> supplier();
   BiConsumer<A, T> accumulator();
   BinaryOperator<A> combiner();
   Function<A, R> finisher();
   Set<Characteristics> characteristics();

   public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
                                             BiConsumer<R, T> accumulator,
                                             BinaryOperator<R> combiner,
                                             Characteristics... characteristics) {
       Objects.requireNonNull(supplier);
       Objects.requireNonNull(accumulator);
       Objects.requireNonNull(combiner);
       Objects.requireNonNull(characteristics);
       Set<Characteristics> cs = (characteristics.length == 0)
                                 ? Collectors.CH_ID
                                 : Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH,
                                                                          characteristics));
       return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, cs);
   }

   public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,                   //A get()
                                                BiConsumer<A, T> accumulator,           //void accept(A a, T t);
                                                BinaryOperator<A> combiner,             //A apply(A t, A u);
                                                Function<A, R> finisher,                //R apply(A a);
                                                Characteristics... characteristics) {
       Objects.requireNonNull(supplier);
       Objects.requireNonNull(accumulator);
       Objects.requireNonNull(combiner);
       Objects.requireNonNull(finisher);
       Objects.requireNonNull(characteristics);
       Set<Characteristics> cs = Collectors.CH_NOID;
       if (characteristics.length > 0) {
           cs = EnumSet.noneOf(Characteristics.class);
           Collections.addAll(cs, characteristics);
           cs = Collections.unmodifiableSet(cs);
       }
       return new Collectors.CollectorImpl<>(supplier, accumulator, combiner, finisher, cs);
   }

   enum Characteristics {
       CONCURRENT,
       UNORDERED,
       IDENTITY_FINISH
   }
}
collector由四个方法组成和一个特性组成

组成	        作用
Supplier	     创建一个新的结果容器
accumulator	  将一个新的元素（流中的元素）加入到结果容器中
combiner	     接受两个中间的结果容器，将它们合并成一个（并行流的时候）
finisher	     将结果容器转换成另一个类型（可选的）
characteristics 是一个枚举特性集合，决定某些操作过程的特性，比如是否是并行的，是否需要转换结果容器，是否是有序的，这些特性用来进行简化操作，
					提供更好的性能。

一共有三个特性，在定义的时候可以选几个来组成这个集合，它们是：

CONCURRENT ：意味着这个同一个容器可以被多个线程调用accumulator方法进行操作
UNORDERED：意味着这个聚合操作不会保留元素的出现顺序，一般是来说最后的结果容器是无序的（比如Set）才会使用
IDENTITY_FINISH：意味着finisher方法是Function.identity(),可以被省略，如果设置了的话，需要A类型可以能强制地转换成R类型，否则会抛出异常。
关于Collector的四个方法，这里用一个流程图来解释这个过程

3.3 举例解释Collector四个方法
下面通过Collectors里面提供的常见方法来详细地说明Collector的组成

3.3.1 Example1- toList()
首先来看toList()方法的组成


public static <T> Collector<T, ?, List<T>> toList() {
   return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                              (left, right) -> { left.addAll(right); return left; },
                              CH_ID);
}
对于这个方法实现来说

supplier是 () -> ArrayList::new，提供的容器类型（A）是ArrayList
accumulator是List::add，将元素item加入到arrayList容器中,即
(intermediateCollector, item) -> intermediateCollector.add(item)

combiner是将两个容器arrayList合并
(left, right) -> { left.addAll(right); return left;}

finisher是啥也不做，combiner之后的结果就直接返回来，所以R也是ArrayList的类型
characteristic是
Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));

IDENTITY_FINISH这个特性是说，不执行finisher函数，直接返回combiner之后的结果容器

3.3.2 Example2- joining()
joining有三个方法重载，我们这里先看最直观的一个，它的实现是

public static Collector<CharSequence, ?, String> joining() {
   return new CollectorImpl<CharSequence, StringBuilder, String>(
           StringBuilder::new, StringBuilder::append,
           (r1, r2) -> { r1.append(r2); return r1; },
           StringBuilder::toString, CH_NOID);
}
supplier是StringBuilder::new，即：
() -> new StringBuilder();

容器A的类型是StringBuilder

accumulator是StringBuilder::append， 即
（intermediate, current）-> intermediare.append(current);

combiner是
(r1, r2) -> { r1.append(r2); return r1; }

即对于两个中间的结果stringBuilder来说，combiner做的事情就是合并两个stringBuilder，变成一个stringBuilder

finisher是StringBuilder::toString，对于最后的容器StringBuilder，finisher会将它转换成String的类型，因此R的类型是String
characteristic是一个emptySet(),不包含任何特性
Collections.emptySet();


【Collectors类】
1. 前言
Collection移除元素操作 相关的文章中提到了 Collectors 。相信很多同学对这个比较感兴趣，那我们今天就来研究一下 Collectors 。

2. Collectors 的作用
Collectors 是 Java 8 加入的操作类，位于 java.util.stream 包下。它会根据不同的策略将元素收集归纳起来，比如最简单常用的是将元素装
入Map、Set、List 等可变容器中。特别对于 Java 8 Stream Api 来说非常有用。它提供了collect() 方法来对 Stream 流进行终结操作派生出基于各种策略
的结果集。如：

我们就借助于 Stream 来熟悉一下 Collectors 吧。例子：

    List<String> servers = new ArrayList<>();
        servers.add("Felordcn");
        servers.add("Tomcat");
        servers.add("Jetty");
        servers.add("Undertow");
        servers.add("Resin");
3. Java 8 中 Collectors 的方法
Collectors 提供了一系列的静态方法供我们使用，通常情况我们静态导入即可使用。接下来我们来看看都提供了哪些方法吧。

3.1 类型归纳
这是一个系列，作用是将元素分别归纳进可变容器 List、Map、Set、Collection 或者ConcurrentMap 。

    Collectors.toList();
    Collectors.toMap();
    Collectors.toSet();
    Collectors.toCollection();
    Collectors.toConcurrentMap();
我们可以根据以上提供的 API 使用 Stream 的 collect 方法中的转换为熟悉的集合容器。非常简单这里不再演示。

3.2 joining
将元素以某种规则连接起来。该方法有三种重载 joining(CharSequence delimiter) 
和 joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

 //   输出 FelordcnTomcatJettyUndertowResin
 servers.stream().collect(Collectors.joining());

 //   输出 Felordcn,Tomcat,Jetty,Undertow,Resin
 servers.stream().collect(Collectors.joining("," ));

 //   输出 [Felordcn,Tomcat,Jetty,Undertow,Resin]
 servers.stream().collect(Collectors.joining(",", "[", "]")); 
用的比较多的是读取 HttpServletRequest 中的 body ：

  HttpServletRequest.getReader().lines().collect(Collectors.joining());
3.3 collectingAndThen
该方法先执行了一个归纳操作，然后再对归纳的结果进行 Function 函数处理输出一个新的结果。

 // 比如我们将servers joining 然后转成大写，结果为： FELORDCN,TOMCAT,JETTY,UNDERTOW,RESIN   
 servers.stream.collect(Collectors.collectingAndThen(Collectors.joining(","), String::toUpperCase));
3.4 groupingBy
按照条件对元素进行分组，和 SQL 中的 group by 用法有异曲同工之妙，通常也建议使用 Java 进行分组处理以减轻数据库压力。groupingBy 也有三个重载方法
我们将 servers 按照长度进行分组:

// 按照字符串长度进行分组    符合条件的元素将组成一个 List 映射到以条件长度为key 的 Map<Integer, List<String>> 中
servers.stream.collect(Collectors.groupingBy(String::length))
如果我不想 Map 的 value 为 List 怎么办？ 上面的实现实际上调用了下面的方式：

 //Map<Integer, Set<String>>
 servers.stream.collect(Collectors.groupingBy(String::length, Collectors.toSet()))
我要考虑同步安全问题怎么办？ 当然使用线程安全的同步容器啊，那前两种都用不成了吧！ 别急！ 我们来推断一下，其实第二种等同于下面的写法:

 Supplier<Map<Integer,Set<String>>> mapSupplier = HashMap::new;
 Map<Integer,Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));
这就非常好办了，我们提供一个同步 Map 不就行了，于是问题解决了：

 Supplier<Map<Integer, Set<String>>> mapSupplier = () -> Collections.synchronizedMap(new HashMap<>());
 Map<Integer, Set<String>> collect = servers.stream.collect(Collectors.groupingBy(String::length, mapSupplier, Collectors.toSet()));
其实同步安全问题 Collectors 的另一个方法 groupingByConcurrent 给我们提供了解决方案。用法和 groupingBy 差不多。

3.5 partitioningBy
partitioningBy 我们在本文开头的提到的文章中已经见识过了，可以看作 groupingBy 的一个特例，基于断言（Predicate）策略分组。这里不再举例说明。

3.6 counting
该方法归纳元素的的数量，非常简单，不再举例说明。

3.7 maxBy/minBy
这两个方法分别提供了查找大小元素的操作，它们基于比较器接口 Comparator 来比较 ，返回的是一个 Optional 对象。 我们来获取 servers 中最小长度的元素:

 // Jetty  
Optional<String> min = servers.stream.collect(Collectors.minBy(Comparator.comparingInt(String::length)));
这里其实 Resin 长度也是最小，这里遵循了 "先入为主" 的原则 。当然 Stream.min() 可以很方便的获取最小长度的元素。maxBy 同样的道理。

3.8 summingInt/Double/Long
用来做累加计算。计算元素某个属性的总和,类似 Mysql 的 sum 函数，比如计算各个项目的盈利总和、计算本月的全部工资总和等等。我们这里就计算一下 servers 中字符串的长度之和 （为了举例不考虑其它写法）。

 // 总长度 32 
 servers.stream.collect(Collectors.summingInt(s -> s.length()));
3.9 summarizingInt/Double/Long
如果我们对 3.6章节-3.8章节 的操作结果都要怎么办？难不成我们搞5个 Stream 流吗？ 所以就有了 summarizingInt、summarizingDouble、summarizingLong 三个方法。
这三个方法通过对元素某个属性的提取，会返回对元素该属性的统计数据对象，分别对应 IntSummaryStatistics、DoubleSummaryStatistics、LongSummaryStatistics。我们对 servers 中元素的长度进行统计：

 DoubleSummaryStatistics doubleSummaryStatistics = servers.stream.collect(Collectors.summarizingDouble(String::length));
  // {count=5, sum=32.000000, min=5.000000, average=6.400000, max=8.000000}
  System.out.println("doubleSummaryStatistics.toString() = " + doubleSummaryStatistics.toString());
结果 DoubleSummaryStatistics 中包含了 总数，总和，最小值，最大值，平均值 五个指标。

3.10 mapping
该方法是先对元素使用 Function 进行再加工操作，然后用另一个Collector 归纳。比如我们先去掉 servers 中元素的首字母，然后将它们装入 List 。

 // [elordcn, omcat, etty, ndertow, esin]
 servers.stream.collect(Collectors.mapping(s -> s.substring(1), Collectors.toList()));
有点类似 Stream 先进行了 map 操作再进行 collect ：

 servers.stream.map(s -> s.substring(1)).collect(Collectors.toList());
3.11 reducing
这个方法非常有用！但是如果要了解这个就必须了解其参数 BinaryOperator<T> 。 这是一个函数式接口，是给两个相同类型的量，返回一个跟这两个量相同类型的一个结果，伪表达式为 (T,T) -> T。默认给了两个实现 maxBy 和 minBy ，根据比较器来比较大小并分别返回最大值或者最小值。当然你可以灵活定制。然后 reducing 就很好理解了，元素两两之间进行比较根据策略淘汰一个，随着轮次的进行元素个数就是 reduce 的。那这个有什么用处呢？ Java 官方给了一个例子：统计每个城市个子最高的人。

  Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
     Map<String, Optional<Person>> tallestByCity = people.stream()
                          .collect(Collectors.groupingBy(Person::getCity, Collectors.reducing(BinaryOperator.maxBy(byHeight))));
结合最开始给的例子你可以使用 reducing 找出最长的字符串试试。

上面这一层是根据 Height 属性找最高的 Person ，而且如果这个属性没有初始化值或者没有数据，很有可能拿不到结果所以给出的是 Optional<Person>。 如果我们给出了 identity 作一个基准值，那么我们首先会跟这个基准值进行 BinaryOperator 操作。
比如我们给出高于 2 米 的人作为 identity。 我们就可以统计每个城市不低于 2 米 而且最高的那个人，当然如果该城市没有人高于 2 米则返回基准值identity ：

 Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
 Person identity= new Person();
           identity.setHeight(2.);
           identity.setName("identity");
     Map<String, Person> collect = persons.stream()
                        .collect(Collectors.groupingBy(Person::getCity, Collectors.reducing(identity, BinaryOperator.maxBy(byHeight))));
这时候就确定一定会返回一个 Person 了，最起码会是基准值identity 不再是 Optional 。

还有些情况，我们想在 reducing 的时候把 Person 的身高先四舍五入一下。这就需要我们做一个映射处理。定义一个 Function<? super T, ? extends U> mapper 来干这个活。那么上面的逻辑就可以变更为：

   Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
        Person identity = new Person();
        identity.setHeight(2.);
        identity.setName("identity");
        // 定义映射 处理 四舍五入
        Function<Person, Person> mapper = ps -> {
            Double height = ps.getHeight();

            BigDecimal decimal = new BigDecimal(height);
            Double d = decimal.setScale(1, BigDecimal.ROUND_HALF_UP).doubleValue();
            ps.setHeight(d);
            return ps;
        };
        Map<String, Person> collect = persons.stream()
                .collect(Collectors.groupingBy(Person::getCity, Collectors.reducing(identity, mapper, BinaryOperator.maxBy(byHeight))));




一、概述
Collector是专门用来作为Stream的collect方法的参数的。

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    <R, A> R collect(Collector<? super T, A, R> collector);
}
而Collectors是作为生产具体Collector的工具类。

二、Collector
Collector主要包含五个参数，它的行为也是由这五个参数来定义的，如下所示：

//public interface Collector<T, A, R> {
    // supplier参数用于生成结果容器，容器类型为A
    Supplier<A> supplier();
    
    // accumulator用于消费元素，也就是归纳元素，这里的T就是元素，它会将流中的元素一个一个与结果容器A发生操作
    BiConsumer<A, T> accumulator();
    
    // combiner用于两个两个合并并行执行的线程的执行结果，将其合并为一个最终结果A
    BinaryOperator<A> combiner();
    
    // finisher用于将之前整合完的结果R转换成为A
    Function<A, R> finisher();
    
    // characteristics表示当前Collector的特征值，这是个不可变Set
    Set<Characteristics> characteristics();
}
Collector拥有两个of方法用于生成Collector实例，其中一个拥有上面所有五个参数，另一个四个参数，不包括finisher。

//public interface Collector<T, A, R> {
    // 四参方法，用于生成一个Collector，T代表流中的一个一个元素，R代表最终的结果
    public static<T, R> Collector<T, R, R> of(Supplier<R> supplier,
                                              BiConsumer<R, T> accumulator,
                                              BinaryOperator<R> combiner,
                                              Characteristics... characteristics) {//...}
    // 五参方法，用于生成一个Collector，T代表流中的一个一个元素，A代表中间结果，R代表最终结果，finisher用于将A转换为R                                          
    public static<T, A, R> Collector<T, A, R> of(Supplier<A> supplier,
                                                 BiConsumer<A, T> accumulator,
                                                 BinaryOperator<A> combiner,
                                                 Function<A, R> finisher,
                                                 Characteristics... characteristics) {//...}                                              
}
Characteristics：这个特征值是一个枚举，拥有三个值：CONCURRENT（多线程并行），UNORDERED（无序），IDENTITY_FINISH（无需转换结果）。
其中四参of方法中没有finisher参数，所以必有IDENTITY_FINISH特征值。

三、Collectors
Collectors是一个工具类，是JDK预实现Collector的工具类，它内部提供了多种Collector，我们可以直接拿来使用，非常方便。

5.6.1 toCollection
将流中的元素全部放置到一个集合中返回，这里使用Collection，泛指多种集合。

public class CollectorsTest {
    public static void toCollectionTest(List<String> list) {
        List<String> ll = list.stream().collect(Collectors.toCollection(LinkedList::new));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        toCollectionTest(list);
    }
}
5.6.2 toList
将流中的元素放置到一个列表集合中去。这个列表默认为ArrayList。

public class CollectorsTest {
    public static void toListTest(List<String> list) {
        List<String> ll = list.stream().collect(Collectors.toList());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        toListTest(list);
    }
}
5.6.3 toSet
将流中的元素放置到一个无序集set中去。默认为HashSet。

public class CollectorsTest {
    public static void toSetTest(List<String> list) {
        Set<String> ss = list.stream().collect(Collectors.toSet());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        toSetTest(list);
    }
}
5.6.4 joining
joining的目的是将流中的元素全部以字符序列的方式连接到一起，可以指定连接符，甚至是结果的前后缀。

public class CollectorsTest {
    public static void joiningTest(List<String> list){
        // 无参方法
        String s = list.stream().collect(Collectors.joining());
        System.out.println(s);
        // 指定连接符
        String ss = list.stream().collect(Collectors.joining("-"));
        System.out.println(ss);
        // 指定连接符和前后缀
        String sss = list.stream().collect(Collectors.joining("-","S","E"));
        System.out.println(sss);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        joiningTest(list);
    }
}
执行结果：

1234567891101212121121asdaa3e3e3e2321eew
123-456-789-1101-212121121-asdaa-3e3e3e-2321eew
S123-456-789-1101-212121121-asdaa-3e3e3e-2321eewE
StringJoiner：这是一个字符串连接器，可以定义连接符和前后缀，正好适用于实现第三种joining方法。

5.6.5 mapping
这个映射是首先对流中的每个元素进行映射，即类型转换，然后再将新元素以给定的Collector进行归纳。

public class CollectorsTest {
    public static void mapingTest(List<String> list){
        List<Integer> ll = list.stream().limit(5).collect(Collectors.mapping(Integer::valueOf,Collectors.toList()));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        mapingTest(list);
    }
}
实例中截取字符串列表的前5个元素，将其分别转换为Integer类型，然后放到一个List中返回。

5.6.6 collectingAndThen
该方法是在归纳动作结束之后，对归纳的结果进行再处理。

public class CollectorsTest {
    public static void collectingAndThenTest(List<String> list){
        int length = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(),e -> e.size()));
        System.out.println(length);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        collectingAndThenTest(list);
    }
}
执行结果为：

8
5.6.7 counting
该方法用于计数。

public class CollectorsTest {
    public static void countingTest(List<String> list){
        long size = list.stream().collect(Collectors.counting());
        System.out.println(size);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        countingTest(list);
    }
}
结果：

8
5.6.8 minBy/maxBy
生成一个用于获取最小/最大值的Optional结果的Collector。

public class CollectorsTest {
    public static void maxByAndMinByTest(List<String> list){
        System.out.println(list.stream().collect(Collectors.maxBy((a,b) -> a.length()-b.length())));
        System.out.println(list.stream().collect(Collectors.minBy((a,b) -> a.length()-b.length())));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        maxByAndMinByTest(list);
    }
}
执行结果为：

Optional[212121121]
Optional[123]
5.6.9 summingInt/summingLong/summingDouble
生成一个用于求元素和的Collector，首先通过给定的mapper将元素转换类型，然后再求和。

参数的作用就是将元素转换为指定的类型，最后结果与转换后类型一致。

public class CollectorsTest {
    public static void summingTest(List<String> list){
        int i = list.stream().limit(3).collect(Collectors.summingInt(Integer::valueOf));
        long l = list.stream().limit(3).collect(Collectors.summingLong(Long::valueOf));
        double d = list.stream().limit(3).collect(Collectors.summingDouble(Double::valueOf));
        System.out.println(i +"\n" +l + "\n" + d);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        summingTest(list);
    }
}
执行结果为：

1368
1368
1368.0
5.6.10 averagingInt/averagingLong/averagingDouble
生成一个用于求元素平均值的Collector，首选通过参数将元素转换为指定的类型。

参数的作用就是将元素转换为指定的类型，求平均值涉及到除法操作，结果一律为Double类型。

public class CollectorsTest {
    public static void averagingTest(List<String> list){
        double i = list.stream().limit(3).collect(Collectors.averagingInt(Integer::valueOf));
        double l = list.stream().limit(3).collect(Collectors.averagingLong(Long::valueOf));
        double d = list.stream().limit(3).collect(Collectors.averagingDouble(Double::valueOf));
        System.out.println(i +"\n" +l + "\n" + d);
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        averagingTest(list);
    }
}
执行结果为：

456.0
456.0
456.0
5.6.11 reducing
reducing方法有三个重载方法，其实是和Stream里的三个reduce方法对应的，二者是可以替换使用的，作用完全一致，也是对流中的元素做统计归纳作用。

//public final class Collectors {
    // 无初始值的情况，返回一个可以生成Optional结果的Collector
    public static <T> Collector<T, ?, Optional<T>> reducing(BinaryOperator<T> op) {/*...*/}
    // 有初始值的情况，返回一个可以直接产生结果的Collector
    public static <T> Collector<T, ?, T> reducing(T identity, BinaryOperator<T> op) {/*...*/}
    // 有初始值，还有针对元素的处理方案mapper，生成一个可以直接产生结果的Collector，元素在执行结果操作op之前需要先执行mapper进行元素转换操作
    public static <T, U> Collector<T, ?, U> reducing(U identity,
                                    Function<? super T, ? extends U> mapper,
                                    BinaryOperator<U> op) {/*...*/}
}
实例：
/*
public class CollectorsTest {
    public static void reducingTest(List<String> list){
        System.out.println(list.stream().limit(4).map(String::length).collect(Collectors.reducing(Integer::sum)));
        System.out.println(list.stream().limit(3).map(String::length).collect(Collectors.reducing(0, Integer::sum)));
        System.out.println(list.stream().limit(4).collect(Collectors.reducing(0,String::length,Integer::sum)));
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        reducingTest(list);
    }
}
Optional[13]
9
13
效果可参见Java流式操作系列-Stream

5.6.12 groupingBy
这个方法是用于生成一个拥有分组功能的Collector，它也有三个重载方法：

//public final class Collectors {
    // 只需一个分组参数classifier，内部自动将结果保存到一个map中，每个map的键为?类型（即classifier的结果类型），值为一个list，这个list中保存在属于这个组的元素。
    public static <T, K> Collector<T, ?, Map<K, List<T>>> groupingBy(
            Function<? super T, ? extends K> classifier) {/*...*/}
    // 在上面方法的基础上增加了对流中元素的处理方式的Collector，比如上面的默认的处理方法就是Collectors.toList()
    public static <T, K, A, D>Collector<T, ?, Map<K, D>> groupingBy(
            Function<? super T, ? extends K> classifier,Collector<? super T, A, D> downstream) {/*...*/}
    // 在第二个方法的基础上再添加了结果Map的生成方法。
    public static <T, K, D, A, M extends Map<K, D>>
        Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                      Supplier<M> mapFactory,
                                      Collector<? super T, A, D> downstream) {/*...*/}
}
实例：

public class CollectorsTest {
    public static void groupingByTest(List<String> list){
        Map<Integer,List<String>> s = list.stream().collect(Collectors.groupingBy(String::length));
        Map<Integer,List<String>> ss = list.stream().collect(Collectors.groupingBy(String::length, Collectors.toList()));
        Map<Integer,Set<String>> sss = list.stream().collect(Collectors.groupingBy(String::length,HashMap::new,Collectors.toSet()));
        System.out.println(s.toString() + "\n" + ss.toString() + "\n" + sss.toString());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        groupingByTest(list);
    }
}
执行结果为：

{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
{3=[123, 456, 789], 4=[1101], 5=[asdaa], 6=[3e3e3e], 7=[2321eew], 9=[212121121]}
groupingBy方法还有并发版的groupingByConcurrent，功能基本一致，只是返回的Collector是并行的。

5.6.13 partitioningBy
该方法将流中的元素按照给定的校验规则的结果分为两个部分，放到一个map中返回，map的键是Boolean类型，值为元素的列表List。

该方法有两个重载方法：

//public final class Collectors {
    // 只需一个校验参数predicate
    public static <T>
        Collector<T, ?, Map<Boolean, List<T>>> partitioningBy(Predicate<? super T> predicate) {/*...*/}
    // 在上面方法的基础上增加了对流中元素的处理方式的Collector，比如上面的默认的处理方法就是Collectors.toList()
    public static <T, D, A>
        Collector<T, ?, Map<Boolean, D>> partitioningBy(Predicate<? super T> predicate,
                                                        Collector<? super T, A, D> downstream) {/*...*/}
}
实例：

public class CollectorsTest {
    public static void partitioningByTest(List<String> list){
        Map<Boolean,List<String>> map = list.stream().collect(Collectors.partitioningBy(e -> e.length()>5));
        Map<Boolean,Set<String>> map2 = list.stream().collect(Collectors.partitioningBy(e -> e.length()>6,Collectors.toSet()));
        System.out.println(map.toString() + "\n" + map2.toString());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        partitioningByTest(list);
    }
}
执行结果：

{false=[123, 456, 789, 1101, asdaa], true=[212121121, 3e3e3e, 2321eew]}
{false=[123, 456, 1101, 789, 3e3e3e, asdaa], true=[212121121, 2321eew]}
5.6.14 toMap
toMap方法是根据给定的键生成器和值生成器生成的键和值保存到一个map中返回，键和值的生成都依赖于元素，可以指定出现重复键时的处理方案和保存结果的map。

//public final class Collectors {
    // 指定键和值的生成方式keyMapper和valueMapper
    public static <T, K, U>
        Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                        Function<? super T, ? extends U> valueMapper) {/*...*/}
    // 在上面方法的基础上增加了对键发生重复时处理方式的mergeFunction，比如上面的默认的处理方法就是抛出异常
    public static <T, K, U>
        Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                        Function<? super T, ? extends U> valueMapper,
                                        BinaryOperator<U> mergeFunction) {/*...*/}
    // 在第二个方法的基础上再添加了结果Map的生成方法。
    public static <T, K, U, M extends Map<K, U>>
        Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction,
                                    Supplier<M> mapSupplier) {//...
   	 }
    }
}
实例：

public class CollectorsTest {
    public static void toMapTest(List<String> list){
        Map<String,String> map = list.stream().limit(3).collect(Collectors.toMap(e -> e.substring(0,1),e -> e));
        Map<String,String> map1 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b));
        Map<String,String> map2 = list.stream().collect(Collectors.toMap(e -> e.substring(0,1),e->e,(a,b)-> b,HashMap::new));
        System.out.println(map.toString() + "\n" + map1.toString() + "\n" + map2.toString());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        toMapTest(list);
    }
}
执行结果：

{1=123, 4=456, 7=789}
{a=asdaa, 1=1101, 2=2321eew, 3=3e3e3e, 4=456, 7=789}
{a=asdaa, 1=1101, 2=2321eew, 3=3e3e3e, 4=456, 7=789}
第一种方式中，如果不添加limit限制，就会抛出异常。

还有并发的版本：toConcurrentMap,同样三种重载方法，与toMap基本一致，只是它最后使用的map是并发Map:ConcurrentHashMap。

5.6.15 summarizingInt/summarizingLong/summarizingDouble
这三个方法适用于汇总的，返回值分别是IntSummaryStatistics，LongSummaryStatistics，DoubleSummaryStatistics。

在这些返回值中包含有流中元素的指定结果的数量、和、最大值、最小值、平均值。所有仅仅针对数值结果。

public class CollectorsTest {
    public static void summarizingTest(List<String> list){
        IntSummaryStatistics intSummary = list.stream().collect(Collectors.summarizingInt(String::length));
        LongSummaryStatistics longSummary = list.stream().limit(4).collect(Collectors.summarizingLong(Long::valueOf));
        DoubleSummaryStatistics doubleSummary = list.stream().limit(3).collect(Collectors.summarizingDouble(Double::valueOf));
        System.out.println(intSummary.toString() + "\n" + longSummary.toString() + "\n" + doubleSummary.toString());
    }
    public static void main(String[] args) {
        List<String> list = Arrays.asList("123","456","789","1101","212121121","asdaa","3e3e3e","2321eew");
        summarizingTest(list);
    }
}
执行结果：

IntSummaryStatistics{count=8, sum=40, min=3, average=5.000000, max=9}
LongSummaryStatistics{count=4, sum=2469, min=123, average=617.250000, max=1101}
DoubleSummaryStatistics{count=3, sum=1368.000000, min=123.000000, average=456.000000, max=789.000000}
最后我们可以从返回的汇总实例中获取到想要的汇总结果。

四、总结
整个Collectors工具类就是在为Collector服务，用于创建各种不同的Collector。部分功能与Stream中的方法重合了，为了简化代码，完全不必采用Collectors实现，优先Stream方法。




*/